package com.hoshiibuy.cas.client;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.pac4j.cas.client.CasClient;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;

import com.hoshiibuy.cas.client.listener.HoshiibuyCasSessionListener;
import com.hoshiibuy.cas.client.pac4j.handler.HoshiibuyCasLogoutHandler;
import com.hoshiibuy.cas.client.realm.HoshiibuyCasRealm;
import com.hoshiibuy.cas.client.session.HoshiibuyCasSessionDao;
import com.hoshiibuy.cas.client.session.HoshiibuyCasSessionFactory;
import com.hoshiibuy.cas.client.session.HoshiibuySessionTicketStore;
import com.hoshiibuy.configuration.HoshiibuyConfiguration;
import com.hoshiibuy.sys.api.UserApi;

import io.buji.pac4j.context.ShiroSessionStore;
import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.LogoutFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.subject.Pac4jSubjectFactory;

/**
 * Shiro集成Cas配置
 *
 */
@Configuration
public class ShiroCasConfiguration {

	private static final Logger logger = LoggerFactory.getLogger(ShiroCasConfiguration.class);

	@Bean
	Config getConfig(HoshiibuyConfiguration hoshiibuyConfiguration, ShiroSessionStore sessionStore,
			HoshiibuyCasLogoutHandler hoshiibuyCasLogoutHandler) {
		CasConfiguration casConfiguration = new CasConfiguration(
				hoshiibuyConfiguration.getCas().getCasServerUrl() + hoshiibuyConfiguration.getCas().getLoginUrl(),
				hoshiibuyConfiguration.getCas().getCasServerUrl());
		casConfiguration.setAcceptAnyProxy(true);
		casConfiguration.setLogoutHandler(hoshiibuyCasLogoutHandler);

		CasClient casClient = new CasClient(casConfiguration);
		casClient.setCallbackUrl(hoshiibuyConfiguration.getCas().getCurrentServerUrlPrefix()
				+ hoshiibuyConfiguration.getCas().getCasFilterUrlPattern() + "?client_name=CasClient");
		casClient.setIncludeClientNameInCallbackUrl(false);

		Clients clients = new Clients(
				hoshiibuyConfiguration.getCas().getCurrentServerUrlPrefix()
						+ hoshiibuyConfiguration.getCas().getCasFilterUrlPattern() + "?client_name=CasClient",
				casClient);
		Config config = new Config(clients);
		config.setSessionStore(sessionStore);

		return config;
	}

	@Bean
	ShiroSessionStore gethoshiibuySessionStore() {
		return new ShiroSessionStore();
	}

	@Bean
	HoshiibuySessionTicketStore getSessionTicketStore(StringRedisTemplate stringRedisTemplate,
			HoshiibuyConfiguration hoshiibuyConfiguration) {
		HoshiibuySessionTicketStore sessionTicketStore = new HoshiibuySessionTicketStore();
		sessionTicketStore.setRedisTemplate(stringRedisTemplate);
		sessionTicketStore.setTimeout(hoshiibuyConfiguration.getCas().getClientSessionTimeout());
		return sessionTicketStore;
	}

	@Bean
	HoshiibuyCasLogoutHandler gethoshiibuyCasLogoutHandler(SessionManager sessionManager,
			HoshiibuySessionTicketStore sessionTicketStore) {
		HoshiibuyCasLogoutHandler handler = new HoshiibuyCasLogoutHandler(sessionTicketStore);
		handler.setDestroySession(true);
		handler.setSessionManager(sessionManager);
		return handler;
	}

	@Bean
	public HoshiibuyCasRealm gethoshiibuyCasRealm(EhCacheManager ehCacheManager, UserApi userApi,
			HoshiibuyConfiguration hoshiibuyConfiguration) {
		HoshiibuyCasRealm realm = new HoshiibuyCasRealm(ehCacheManager, userApi,
				hoshiibuyConfiguration.getSys().getServerName());
		return realm;
	}

	@Bean
	public EhCacheManager getEhCacheManager() {
		EhCacheManager em = new EhCacheManager();
		em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
		return em;
	}

	/**
	 * 注册DelegatingFilterProxy（Shiro）
	 */
	@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
		// 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
		filterRegistration.addInitParameter("targetFilterLifecycle", "true");
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns("/*");
		filterRegistration.setDispatcherTypes(EnumSet.allOf(DispatcherType.class));

		return filterRegistration;
	}

	@Bean(name = "lifecycleBeanPostProcessor")
	public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	@Bean
	public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
		daap.setProxyTargetClass(true);
		return daap;
	}

	@Bean(name = "securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(HoshiibuyCasRealm hoshiibuyCasRealm,
			DefaultWebSessionManager sessionManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(hoshiibuyCasRealm);
		// 用户授权/认证信息Cache, 采用EhCache 缓存
		securityManager.setCacheManager(getEhCacheManager());
		securityManager.setSubjectFactory(new Pac4jSubjectFactory());
		securityManager.setSessionManager(sessionManager);

		return securityManager;
	}

	@Bean(name = "sessionManager")
	public DefaultWebSessionManager getDefaultWebSessionManager(SessionListener sessionListener, SessionDAO sessionDao,
			SessionFactory sessionFactory, HoshiibuyConfiguration hoshiibuyConfiguration, SimpleCookie cookie) {
		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
		sessionManager.setGlobalSessionTimeout(hoshiibuyConfiguration.getCas().getClientSessionTimeout());
		sessionManager.setSessionValidationSchedulerEnabled(false);
		sessionManager.setSessionListeners(Arrays.asList(sessionListener));
		sessionManager.setSessionDAO(sessionDao);
		sessionManager.setSessionFactory(sessionFactory);
		sessionManager.setSessionIdCookie(cookie);
		return sessionManager;
	}

	@Bean(name = "sessionDao")
	public SessionDAO getSessionDao(StringRedisTemplate redisTemplate, HoshiibuySessionTicketStore sessionTicketStore) {
		HoshiibuyCasSessionDao sessionDao = new HoshiibuyCasSessionDao(redisTemplate);
		sessionDao.setSessionTicketStore(sessionTicketStore);
		return sessionDao;
	}

	@Bean
	public SimpleCookie getCookie() {
		SimpleCookie cookie = new SimpleCookie();
		cookie.setName("j_s_id");
		cookie.setHttpOnly(false);
		cookie.setPath("/");
		return cookie;
	}

	@Bean(name = "sessionListener")
	public SessionListener getSessionListener() {
		return new HoshiibuyCasSessionListener();
	}

	@Bean(name = "sessionFactory")
	public SessionFactory getSessionFactory(HoshiibuyConfiguration hoshiibuyConfiguration) {
		return new HoshiibuyCasSessionFactory(hoshiibuyConfiguration);
	}

	@Bean
	public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
			DefaultWebSecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * 加载shiroFilter权限控制规则
	 */
	private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean,
			HoshiibuyConfiguration hoshiibuyConfiguration) {
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();

		filterChainDefinitionMap.put(hoshiibuyConfiguration.getCas().getCasFilterUrlPattern(), "callback");// shiro集成cas后，首先添加该规则
		filterChainDefinitionMap.put(hoshiibuyConfiguration.getCas().getLogoutUrl(), "logout");

		// 添加hoshiibuy.cas的配置规则
		if (hoshiibuyConfiguration.getCas().getFilterChainDefinition() != null) {
			filterChainDefinitionMap.putAll(hoshiibuyConfiguration.getCas().getFilterChainDefinition());
		}

		// 如果配置了/**，则优先自定义的过滤规则，如果没有配置，则全部由cas过滤
		String common = filterChainDefinitionMap.get("/**");
		if (StringUtils.isBlank(common)) {
			filterChainDefinitionMap.put("/**", "security");
		} else if (!common.equals("anon")) {
			filterChainDefinitionMap.put("/**", "security," + common);
		}

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	}

	/**
	 * ShiroFilter
	 */
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager,
			HoshiibuyConfiguration hoshiibuyConfiguration, StringRedisTemplate redisTemplate,
			DefaultWebSessionManager sessionManager, Config config) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		// SecurityManager，Shiro安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		// Shiro登录页面，这里默认为CAS的登录页面：/login?service=serviceurl
		shiroFilterFactoryBean.setLoginUrl(hoshiibuyConfiguration.getCas().getEncodedLoginUrl());
		shiroFilterFactoryBean.setSuccessUrl(hoshiibuyConfiguration.getCas().getSuccessUrl());
		shiroFilterFactoryBean.setUnauthorizedUrl(hoshiibuyConfiguration.getCas().getUnauthorizedUrl());

		// 添加casFilter到shiroFilter中
		Map<String, Filter> filters = new HashMap<>();
		CallbackFilter callbackFilter = new CallbackFilter();
		callbackFilter.setConfig(config);
		callbackFilter.setDefaultUrl(hoshiibuyConfiguration.getCas().getSuccessUrl());
		filters.put("callback", callbackFilter);

		LogoutFilter logoutFilter = new LogoutFilter();
		logoutFilter.setConfig(config);
		logoutFilter.setDefaultUrl(hoshiibuyConfiguration.getCas().getCurrentServerUrlPrefix()
				+ hoshiibuyConfiguration.getCas().getCasFilterUrlPattern() + "?client_name=CasClient");
		logoutFilter.setCentralLogout(true);
		logoutFilter.setLocalLogout(true);// 销毁本地
		filters.put("logout", logoutFilter);

		SecurityFilter securityFilter = new SecurityFilter();
		securityFilter.setConfig(config);
		securityFilter.setClients("CasClient");
		filters.put("security", securityFilter);

		shiroFilterFactoryBean.setFilters(filters);

		loadShiroFilterChain(shiroFilterFactoryBean, hoshiibuyConfiguration);
		return shiroFilterFactoryBean;
	}

}